from puzzle_board import checkStateValidity, evaluateDistanceHeuristic, moveDict, makeMove
from defined_exceptions import ParameterError



#This does not work very well. It just runs for ages.
# I want to test the heuristic with multiple weights. Start with the target and progressively mush it up
# Keep a dict to ensure moves aren't repeated - store number of moves taken to reach
# Test heuristic at each step and see to what extent it < or > the actual number of moves (maybe in another dict)




class Node:
    def __init__(self, state, steps, cost, target="--OOO-TR-----LG-BA"):
        checkStateValidity(state)
        checkStateValidity(target)
        if not isinstance(steps, list):
            raise ParameterError("steps parameter must be a list.")
        if not isinstance(cost, int):
            raise ParameterError("cost parameter must be an int.")
        
        self.state = state
        self.steps = steps
        self.cost = cost
        
        self.heuristic = evaluateDistanceHeuristic(state, target)
        self.fitness = cost + self.heuristic
        self.target=target
        
    
    def expandNode(self):
        availableMoves = moveDict.values()
        if len(self.steps)>0: #As first node will have empty list
            availableMoves.remove(- self.steps[-1]) #Don't want to undo the previous move.
        
        newNodes = []
        for move in availableMoves:
            newState = makeMove(self.state, move)
            newSteps = self.steps + [move]
            newCost = self.cost + 1
            
            newNodes.append( Node(newState, newSteps, newCost, self.target))
        
        return newNodes

def solveTriplecross(state, target="--OOO-TR-----LG-BA"):
    checkStateValidity(state)
    checkStateValidity(target)
    
    firstNode = Node(state=state, steps=[], cost=0, target=target)
    frontierList = [firstNode]
    exploredDict = {}
    
    from operator import attrgetter
    while True:
        nodeToExpand = min(frontierList, key=attrgetter('fitness'))
        frontierList.remove(nodeToExpand)
        
        print nodeToExpand.state, nodeToExpand.cost, nodeToExpand.heuristic
        if nodeToExpand.state == target:
            return nodeToExpand.steps
        
        exploredDict[nodeToExpand.state] = nodeToExpand.cost
        childNodes = nodeToExpand.expandNode()
        for node in childNodes:
            #I feel like this detail is a bit confused. If a node has been expanded previously, but a lower cost path to
            #that node has been found, how do we note this? I must rewatch the videos.
            if node.state in exploredDict: 
                if exploredDict[node.state] > node.cost:
                    exploredDict[node.state] = node.cost
            else:
                frontierList.append(node)

if __name__ == "__main__":
    res = solveTriplecross('-----BOO-LA-R-GTO-')
    print 
    print len(res)
    print res
    
    i = moveDict.items()
    revMoveDict = {}
    for item in i:
        revMoveDict[item[1]] = item[0]
    for val in res:
        print revMoveDict[val]

        